#include <stdio.h>
#include "agbmusic.h"
#include <stdlib.h>
#include <math.h>
#include "xm.h"

#include <stdarg.h>


/*
一般设置为0，
    可以防止一些其他的bug
    bug可能包括，一些乐器会循环到衰弱，直到没有
    .xm可能需要的是，把音量慢慢调节下来
*/
#define DUMP_LOOP 1

/*
把音色节奏设置导出
不会更改声音采样频率
;*type = R (Rhythm set)
;  p??? = type,vgroup,btmkey
;
;     vgroup : 0 ~ 999                 "vgroup" used as rhythm set
;     btmkey : 0 ~ 127 (C-2 ~ G8)     The low limit of MIDI key number
*/
#define DUMP_TONE_RHYTHM_SET 1
/*
mid会更改声音采样频率
; type = S (Key split tone color)
;  p??? = type,table,vgroup,btmkey
;
;      table : 0 ~ 31                  Key split table number (stated later)
;     vgroup : 0 ~ 999                 "vgroup" used as key split tone color
;     btmkey : 0 ~ 127 (C-2 ~ G8)     The low limit of MIDI key number
*/
#define DUMP_TONE_SPLIT 1

/*
处理音色的点击和释放
*/
#define DUMP_TONE_ATT_REL 1


const u8 clock_tbl[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE
,0xF, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
,0x18, 0x1C, 0x1E, 0x20, 0x24, 0x28, 0x2A, 0x2C, 0x30
,0x34, 0x36, 0x38, 0x3C, 0x40, 0x42, 0x44, 0x48, 0x4C
,0x4E, 0x50, 0x54, 0x58, 0x5A, 0x5C, 0x60, 0, 0, 0
};

//转换成AGB的频率
int freq2agb(int freq,int midkey){
    midkey = 180 - midkey;
    double v = (double)midkey / 12.0;
    return (freq * pow(2.f,v))+0x5;
}

//agb转成默认频率
int agb2freq(int freq,int key){
    double m = (double)(180 - key) / (double)12;
    m = pow(2,m);
    freq = ((double)freq / m) + 0.5;
    return freq;
}

int _MidiKey2Freq(int freq,u32 midkey,u32 fp){
    double tmp;
    tmp = ((double)(180 - midkey)) - ((double)fp / 256.f);
    tmp = pow((double)2.f, tmp / 12.f);
    return (int)((double)freq / tmp);
}

//BIOS实现
int MidiKey2Freq(WaveData*w,u32 midkey,u32 fp){
    int freq = (int)w->freq;
    double tmp;
    tmp = ((double)(180 - midkey)) - ((double)fp / 256.f);
    tmp = pow((double)2.f, tmp / 12.f);
    return (int)((double)freq / tmp);
}
#define M_LN2 0.69314718055994530942
//把c5速度转换成relnote
void c5speed_relnote(int freq,uint8_t *note,uint8_t *fine){
    if(!freq){
        *note = 0;
        *fine = 0;
        return;
    }
    int v = log(freq * (1.0/8363.0))* (12.0 * 128.0 * (1.0 / M_LN2));
    *note = v/128;
    *fine = v&0x7f;
}

int relnote_c5speed(int note,int fine){
    double v = round(pow(2.0, (note * 128.0 + fine) * (1.0 / (12.0 * 128.0))) * 8363.0);
    if(v>UINT32_MAX) return UINT32_MAX;
    if(v<0) return 0;
    return v;
}

static int c5_note_freq(int freq,int note){
    note -= xm_Cn5;
    freq *= pow(2.0,((double)note / 12.0));
    return freq;
}


/* E:\programfiles\AGB\MusicPlayerAGB2000\mp2000\b.gba (2014/12/19 18:16:00)
   起始位置(h): 00003BEC, 结束位置(h): 00003C9F, 长度(h): 000000B4 */

unsigned char ScaleTable[180] = {
	0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB,
	0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB,
	0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB,
	0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB,
	0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB,
	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B,
	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B,
	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B,
	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B,
	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B,
	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B,
	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B,
	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B,
	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B
};

const u32 FreqTable[] = {
0x80000000, 0x879C7C97, 0x8FACD61E, 0x9837F052, 0xA14517CC,
0xAADC0848, 0xB504F334, 0xBFC886BB, 0xCB2FF52A, 0xD744FCCB,
0xE411F03A, 0xF1A1BF39
};

static u32 _key2fr(u32 a1,u32 a2){
    return (u64)a1*(u64)a2 >> 32;
}
//内置软件实现
int MidiKey2fr(WaveData*w,u32 midkey,u32 fp){
    int freq = (int)w->freq;
    fp <<= 24;
    if(midkey>0xb2){
        midkey = 0xb2;
        fp = 0xFF000000;
    }
    int freq1 = FreqTable[ScaleTable[midkey]&0xf]>>(ScaleTable[midkey]>>4);
    int freq2 = FreqTable[ScaleTable[midkey+1]&0xf]>>(ScaleTable[midkey+1]>>4);
    return _key2fr(freq,(freq1 + _key2fr((freq2-freq1),fp)));
}


static int write_wav(const char*name,int rate,u8*wav,int wav_size);
int file_printf(file_t*file,const char*fmt,...){
    va_list ap;
    va_start(ap,fmt);
    char buf[BUFSIZ];
    int len = vsprintf(buf,fmt,ap);
    file_write(buf,len,1,file);
    va_end(ap);
    return len;
}

u8 *rom = 0;
static u8*rom_data(const char*path){
    FILE*file = fopen(path,"rb");
    if(!file)return 0;
    fseek(file,0,SEEK_END);
    int sz = ftell(file);
    fseek(file,0,SEEK_SET);
    u8 *data = (u8*)calloc(1,1024*1024*32);  //32MB
    fread(data,sz,1,file);
    fclose(file);
    return data;
}
static void* fix_rom(void*data){
    s32 dp = (s32)data; /*把0x08000000修复成rom的地址*/
    dp -= 0x08000000;
    if(dp>=0&&dp<1024*1024*32){
        return rom + dp;
    }
    return 0;
}

#define fix_rom(v) fix_rom((void*)(v))

typedef struct{
    u8 *mid;
    int end;
    int wait;
    int channel_num;    //声道编号
    int note_wait;
    int cur_Nxx;
    int note;
    int last_note;
    int setvol;
    int v;
    int keysh;      //
    int tempo;
    int vol;
    int insn;
    int oldinsn;
    int pan;
    WaveData *wd;
    int      base_note;
    ToneDataArr *tone;
    int     att;    //点击
    int     dec;    //衰减值
    int     sus;    //持续值，最低(att)
    int     rel;    //释放值
    int     track_vol;   //轨道声音
    int     old_track_vol;
    int     note_up;
    double  clock;
    MusicPlayerTrack track;

    /*处理bendr,和bend*/
    int bend,bendr;
    uint8_t old_event;  //旧事件
}sound_track;

file_t *insn_file;      //乐器文件
int     insn_num = 0;   //乐器数量
int     dump_index = 0; //导出音乐索引，如果为0，则全部导出
//file_t *cur_track;      //当前轨迹
//file_t *tracks_file[0x20];  //所有声道
file_t *mid_file;
file_t *pat_file;

ToneDataArr *cur_toneDataArr;
sound_track *cur_track;
static sound_track tracks[0x20];
static file_t *agb_track[0x10] = {0};
/*
tempo
会*=2
表示每帧添加多少tempo，如果溢出150，则执行一个row,
*/
int tempo = 0;
int oldtempo = 0;
double tempo_speed;

static double tempo_second(int agb){
    agb *= 2;
    double v = (double)150/ (double)agb; //多少帧执行一个节拍
    v = 60/v;                            //一秒多少节拍
    return v;
}

/*
处理gba音乐
需要查找表

*/
/* E:\programfiles\AGB\MusicPlayerAGB2000\mp2000\SoundMon.elf (2014/12/19 17:44:55)
   起始位置(h): 000046A4, 结束位置(h): 000046BD, 长度(h): 0000001A */

unsigned char rawData[26] = {
	0x00, 0xB5, 0x00, 0x04, 0x07, 0x4A, 0x08, 0x49, 0x40, 0x0B, 0x40, 0x18,
	0x83, 0x88, 0x59, 0x00, 0xC9, 0x18, 0x89, 0x00, 0x89, 0x18, 0x0A, 0x68,
	0x01, 0x68
};
static u32 find_rom(){
    int i=0;
    int ci = 0;
    for(i=0;i<1024*1024*32;i++){
        for(ci=0;ci<sizeof(rawData);ci++){
            if(rom[i+ci]!=rawData[ci]){
                /*不相等*/
                break;
            }
        }
        if(ci==sizeof(rawData)){
            //全部相等
            return i + 0x08000000;
        }
    }
    return 0;
}


typedef int (*mid_event_fn_t)(u8*mid);
static const char*key_name[] = {
    "CnM2",
	"CsM2",
	"DnM2",
	"DsM2",
	"EnM2",
	"FnM2",
	"FsM2",
	"GnM2",
	"GsM2",
	"AnM2",
	"AsM2",
	"BnM2",
	"CnM1",
	"CsM1",
	"DnM1",
	"DsM1",
	"EnM1",
	"FnM1",
	"FsM1",
	"GnM1",
	"GsM1",
	"AnM1",
	"AsM1",
	"BnM1",
	"Cn0",
	"Cs0",
	"Dn0",
	"Ds0",
	"En0",
	"Fn0",
	"Fs0",
	"Gn0",
	"Gs0",
	"An0",
	"As0",
	"Bn0",
	"Cn1",
	"Cs1",
	"Dn1",
	"Ds1",
	"En1",
	"Fn1",
	"Fs1",
	"Gn1",
	"Gs1",
	"An1",
	"As1",
	"Bn1",
	"Cn2",
	"Cs2",
	"Dn2",
	"Ds2",
	"En2",
	"Fn2",
	"Fs2",
	"Gn2",
	"Gs2",
	"An2",
	"As2",
	"Bn2",
	"Cn3",
	"Cs3",
	"Dn3",
	"Ds3",
	"En3",
	"Fn3",
	"Fs3",
	"Gn3",
	"Gs3",
    //		  440Hz
	"An3",
	"As3",
	"Bn3",
	"Cn4",
	"Cs4",
	"Dn4",
	"Ds4",
	"En4",
	"Fn4",
	"Fs4",
	"Gn4",
	"Gs4",
	"An4",
	"As4",
	"Bn4",
	"Cn5",
	"Cs5",
	"Dn5",
	"Ds5",
	"En5",
	"Fn5",
	"Fs5",
	"Gn5",
	"Gs5",
	"An5",
	"As5",
	"Bn5",
	"Cn6",
	"Cs6",
	"Dn6",
	"Ds6",
	"En6",
	"Fn6",
	"Fs6",
	"Gn6",
	"Gs6",
	"An6",
	"As6",
	"Bn6",
	"Cn7",
	"Cs7",
	"Dn7",
	"Ds7",
	"En7",
	"Fn7",
	"Fs7",
	"Gn7",
	"Gs7",
	"An7",
	"As7",
	"Bn7",
	"Cn8",
	"Cs8",
	"Dn8",
	"Ds8",
	"En8",
	"Fn8",
	"Fs8",
	"Gn8",
};

static u8 _wait_data[] = {
    28
	,30
	,32
	,36
	,40
	,42
	,44
	,48
	,52
	,54
	,56
	,60
	,64
	,66
	,68
	,72
	,76
	,78
	,80
	,84
	,88
	,90
	,92
	,96
};

static int _toxm_note(int note,int base_note){
    note -= base_note;   //获得当前键相对基础键的偏移 base_note 是作为声音采样频率
    note += xm_Cn5;                 //xm默认使用C5作为声音采样频率
    return note;
}

static int toxm_note(int note){
    note -= cur_track->base_note;   //获得当前键相对基础键的偏移 base_note 是作为声音采样频率
    note += xm_Cn5;                 //xm默认使用C5作为声音采样频率
    return note;
}

static int _key(u8*mid){
    cur_track->note = mid[0];
    file_printf(agb_track[cur_track->channel_num],".byte %s",note_name[mid[0]]);
    if(mid[1]>=0x0&&mid[1]<=127){
        file_printf(agb_track[cur_track->channel_num]," ,%d",mid[1]);
        cur_track->setvol = mid[1];
        if(mid[2]>=0x0&&mid[2]<=127){
            //不知道是啥参数
            //ch[4] += mid[2]
            file_printf(agb_track[cur_track->channel_num]," ,%d",mid[2]);
            return 3;
        }
        return 2;
    }
    file_printf(agb_track[cur_track->channel_num],"\n");

    //测试
    if(cur_track->channel_num==1){
        /*
        21024
        15750
        18730
        23598
        21024

        keysh 0x10
        52977
        39688
        47197
        59462
        */
        if(cur_track->wd){
            int freq = MidiKey2Freq(cur_track->wd,mid[0]+0x10,0);
            fprintf(stderr,"freq:%d\n",freq);
        }
    }

    return 1;
}

static int _keysh(u8*mid){
    file_printf(agb_track[cur_track->channel_num],".byte KEYSH,%d\n\n",mid[1]);
    return 2;
}

static int _tempo(u8*mid){
    file_printf(agb_track[cur_track->channel_num],".byte TEMPO,%d\n",mid[1]);
    return 2;
}

static int _wait(u8*mid){
    int wait;
    // if(*mid<=W24){
    //     wait = *mid - W00;
    // }
    // else{
    //     int index = *mid-W28;
    //     wait = _wait_data[index];
    // }
    wait = clock_tbl[*mid-W00];

    cur_track->wait = wait;
    file_printf(agb_track[cur_track->channel_num],".byte W%02d\n",wait);

    return 1;
}

static int _goto(u8*mid){
    file_printf(agb_track[cur_track->channel_num],".byte PATT\n");
    file_printf(agb_track[cur_track->channel_num],".int  0x%08X\n",*(u32*)(mid+1));
    return 5;
}

static int _patt(u8*mid){
    file_printf(agb_track[cur_track->channel_num],".byte PATT\n");
    file_printf(agb_track[cur_track->channel_num],".int  0x%08X\n",*(u32*)(mid+1));
    return 5;
}

static int _pend(u8*mid){
    return 1;
}

static int _voice(u8*mid){
    return 2;
}

static int _vol(u8*mid){
    return 2;
}

static int _pan(u8*mid){
    return 2;
}

static int cur_Nxx = 0;
static int _note(u8*mid){
    int wait;
    // if(*mid<=N24){
    //     wait = *mid - N01 + 1;
    // }
    // else{
    //     wait = _wait_data[*mid-N28];
    // }
    int idx = mid[0] - N01 + 1;
    wait = clock_tbl[idx];

    file_printf(agb_track[cur_track->channel_num],".byte %s\n",note_w_name[mid[0]-N01]);

    cur_Nxx = wait;
    cur_track->note_wait = wait;
    cur_track->cur_Nxx = wait;

    return 1;
}

static int _bend(u8*mid){
    return 2;
}

static int _bendr(u8*mid){
    return 2;
}

static int _lfos(u8*mid){
    return 1;
}

static int _lfodl(u8*mid){
    return 1;
}
static int _mod(u8*mid){
    return 1;
}
static int _modt(u8*mid){
    return 1;
}

static int _tune(u8*mid){
    return 1;
}
static int _eot(u8*mid){
    return 1;
}
static int _tie(u8*mid){
    return 1;
}

static int _ply_goto (u8*mid){
    return _goto(mid);
}
static int _ply_patt (u8*mid){
    return _patt(mid);
}
static int _ply_pend (u8*mid){
    return 1;
}
static int _ply_rept (u8*mid){
    return 1;
}
static int _ply_fine (u8*mid){
    //结束
    cur_track->mid = 0;
    file_printf(agb_track[cur_track->channel_num],".byte FINE\n");
    return 1;
}
static int _ply_prio (u8*mid){
    file_printf(agb_track[cur_track->channel_num],".byte PRIO,%d\n",mid[1]);
    return 1;
}
static int _ply_tempo (u8*mid){
    tempo = mid[1];
    tempo_speed = tempo_second(tempo) * 60;
    file_printf(agb_track[cur_track->channel_num],".byte TEMPO,%d\n",mid[1]);
    return 2;
}
static int _ply_keysh (u8*mid){
    cur_track->keysh = mid[1];
    file_printf(agb_track[cur_track->channel_num],".byte KEYSH,%d\n",mid[1]);
    return 2;
}
static int _ply_voice (u8*mid){
    if(cur_track->channel_num==0){
        fprintf(stderr,"ch");
    }
    file_printf(agb_track[cur_track->channel_num],".byte VOICE,%d\n",mid[1]);
    int insn = mid[1];
    ToneDataArr*arr = cur_toneDataArr + insn;
    WaveData *wd =  fix_rom(arr->t.wav);
    cur_track->tone = arr;
    if(arr->t.typ==TONEDATA_TYP_SPL){
        return 2;
    }

    #if DUMP_TONE_RHYTHM_SET
    if(arr->t.typ==0x80){
        //音色表

    }
    #endif


    if(!(arr->t.typ==0||arr->t.typ==1)){
        fprintf(stderr,"no other voice\n");
        return 2;
    }

    cur_track->wd = wd;

    if(wd&&(wd->stat&0xff)==0){
        //把wav数据导出
        //fprintf(stderr,"ToneData:%X\n",arr->t.wav);
        char buf[BUFSIZ];
        int len = sprintf(buf,"%X.wav",arr->t.wav);
        buf[len] = 0;
        /*
        需要计算频率,先获得key键
        频率 = 原始频率 * 2^((180-中心key键)/12)
        */
        double m = (double)(180 - arr->t.key) / (double)12;
        m = pow(2,m);
        int freq = ((double)wd->freq / m) + 0.5;
        int num;
        for(num=0;num<wd->size;num++){
            wd->data[num] += 0x80;
        }

        write_wav(buf,freq,wd->data,wd->size);

        /*
        处理c5速度
        */
        u8 relnote,fine;
        c5speed_relnote(freq,&relnote,&fine);
        int newfreq = relnote_c5speed(relnote+12,fine);

        #if 1
        //生成新的乐器文件
        if(insn_num<90){
            for(num=0;num<wd->size;num++){
                wd->data[num] += 0x80;
            }
            int loop_start = 0;
            int loop_end = 0;
            int size = wd->size;
            #if DUMP_LOOP
            if(wd->stat&0x4000){
                //存在循环,需要一个额外的字节
                loop_start = wd->loop;
                loop_end = wd->size;
                //size += 1;
            }
            #endif
            xm_gen_insn(insn_file,(u32)arr->t.wav,wd->data,size,freq,relnote,fine,loop_start,loop_end);
            insn_num++;
        }
        #endif

        cur_track->insn = insn_num;

        wd->stat |= insn_num;
        cur_track->base_note = arr->t.key;
        //exit(0);
    }
    else if(wd&&(wd->stat&0xff)){
        //设置乐器
        cur_track->insn = (wd->stat&0xff);
        cur_track->base_note = arr->t.key;
    }

    return 2;
}
static int _ply_vol (u8*mid){
    file_printf(agb_track[cur_track->channel_num],".byte VOL,%d\n",mid[1]);
    cur_track->vol = mid[1];
    return 2;
}
static int _ply_pan (u8*mid){
    file_printf(agb_track[cur_track->channel_num],".byte PAN,%d\n",mid[1]);
    cur_track->pan = mid[1];
    return 2;
}
static int _ply_bend (u8*mid){
    file_printf(agb_track[cur_track->channel_num],".byte BEND,%d\n",mid[1]);
    cur_track->track.d[0xe] = mid[1];
    cur_track->track.d[0] |= 0xc;
    cur_track->bend = mid[1];
    cur_track->bend -= 0x40;    //-64~63
    return 2;
}
static int _ply_bendr (u8*mid){
    file_printf(agb_track[cur_track->channel_num],".byte BENDR,%d\n",mid[1]);
    cur_track->track.d[0xf] = mid[1];
    cur_track->track.d[0] |= 0xc;
    cur_track->bendr = mid[1];
    return 2;
}

static int _bendr(MusicPlayerTrack*track){
    int v = track->d[0xd] + 4*(track->d[0xc]+track->d[0xe]*track->d[0xf]);
    v += track->d[0xa]<<8;
    v += track->d[0xb]<<8;
    if(track->d[0x18]){
        v += 16* track->d[0x16];
    }
    track->d[8] = v>>8;
    track->d[9] = v;
}

static int _ply_lfos (u8*mid){
    return 1;
}
static int _ply_lfodl (u8*mid){
    return 1;
}
static int _ply_mod (u8*mid){
    return 1;
}
static int _ply_modt (u8*mid){
    return 1;
}
static int _ply_tune (u8*mid){
    return 1;
}
static int _ply_port (u8*mid){
    return 1;
}
static int _ply_endtie_rev01 (u8*mid){
    return 1;
}

static int _SampFreqSe(u8*mid){
    file_printf(agb_track[cur_track->channel_num],".byte TIE,%d\n",mid[1]);
    return 2;
}

static mid_event_fn_t mid_event[0x100] = {
    [CnM2 ... Gn8] = _key,  //一般为N??,TIE,EOT的参数
    [KEYSH] = _ply_keysh,       //设置键的变化(基础,默认应该为60)
    [TEMPO] = _ply_tempo,       //设置速度
    [W00 ... W96] = _wait,  //等待
    [N01 ... N96] = _note,  //设置按键
    [GOTO] = _ply_goto, //跳转表
    [PATT] = _ply_patt, //跳转表
    [PEND] = _ply_pend,
    [VOICE] = _ply_voice,   //音色
    [VOL] = _ply_vol,       //音量
    [PAN] = _ply_pan,       //
    [BEND] = _ply_bend,     //pitch bend (c_v+??)				音高
    [BENDR] = _ply_bendr,   //bend range						弯曲范围
    [LFOS] = _ply_lfos,     //LFO speed							低频振荡速度
    [LFODL] = _ply_lfodl,   //LFO delay							低频振荡衰减
    [MOD] = _ply_mod,       //modulation depth					调制深度
    [MODT] = _ply_modt,     //modulation type					调制类型
    [TUNE] = _ply_tune,     //micro tuning (c_v+??)				微调
    [EOT] = _ply_endtie_rev01,       //End of Tie
    [TIE] = _SampFreqSe,       //
    [_PORT] = _ply_port,
    [FINE] = _ply_fine,
};

typedef struct __attribute__ ((__packed__)){
    u8      id[4];
    u32     size;       //16
    u16     audio_fmt;  //
    u16     channels;   //1
    u32     rate;       //
    u32     brate;      //每秒字节数
    u16     balign;     //块对齐
    u16     bitper;     //采样位数 8
}wav_fmt_t;

typedef struct{
    u8      id[4];      //data;
    u32     size;       //大小
}wav_data_t;

typedef struct{
    u8      id[4];   //RIFF
    u32     size;    //
    u8      typ[4];  //WAVE
    wav_fmt_t   fmt;
    wav_data_t  data;
}wav_t;

static wav_t _wav = {
    "RIFF",
    0,
    "WAVE",
    {
        "fmt ",
        sizeof(wav_fmt_t)-4-4,
        1,
        1,  //声道
        0,0,
        1,  //1字节
        8,  //每秒采样位数
    },
    {
        "data",
    },
};

static int write_wav(const char*name,int rate,u8*wav,int wav_size){
    FILE*file = fopen(name,"wb+");
    fprintf(stderr,"rate:%d",rate);
    _wav.fmt.rate = rate;
    _wav.fmt.brate = rate;
    _wav.data.size = wav_size;
    _wav.size = sizeof(_wav) + wav_size - 8;
    fwrite(&_wav,sizeof(_wav),1,file);
    fwrite(wav,wav_size,1,file);
    fclose(file);
    return 0;
}

static int _add_insn(ToneDataArr*arr,WaveData*wd){
    if(wd->stat&0xff){
        return wd->stat&0xff;
    }
    /*转换为采样频率*/
    int freq = agb2freq(wd->freq,arr->t.key);
    u8 relnote,fine;
    /*转换为xm频率*/
    c5speed_relnote(freq,&relnote,&fine);
    int loop_start = 0;
    int loop_end = 0;
    int size = wd->size;
    #if DUMP_LOOP
    if(wd->stat&0x4000){
        //存在循环，需要加一个字节的大小
        loop_start = wd->loop;
        loop_end = wd->size;
        //size += 1;
    }
    #endif
    

    xm_gen_insn(insn_file,(u32)arr->t.wav,wd->data,size,freq,relnote,fine,loop_start,loop_end);
    insn_num++;
    wd->stat |= insn_num;

    return insn_num;
}

static void song_proc(SongTable*song_table){
    SongHeader *so = fix_rom(song_table->so);
    file_seek(mid_file,0,SEEK_SET);
    file_seek(pat_file,0,SEEK_SET);
    int pat_num = 0;
    int row_num = 0;

    if(so&&so->trks>0){
        //需要存在轨道
        int ch_num;
        ToneDataArr*arr_base = fix_rom(so->tone);
        cur_toneDataArr = arr_base;
        //fprintf(stderr,"song process:%d\n",arr_base);

        #if 1
        //导出mid
        int isend = 0;
        for(ch_num=0;ch_num<so->trks;ch_num++){
            u8 *mid = fix_rom(so->part[ch_num]);
            tracks[ch_num].mid = mid;
            tracks[ch_num].channel_num = ch_num;
            tracks[ch_num].clock = 0.0;
        }
        row_num = -1;

        while(!isend){
            isend = 1;
            row_num++;
            if(row_num==256){
                //把mid数据写入到pat中
                xm_gen_pat(pat_file,mid_file);
                pat_num ++;     //下一个pat
                row_num = 0;
                if(pat_num>=255){
                    //太满了，退出
                    break;
                }
            }
            for(ch_num=0;ch_num<so->trks;ch_num++){

                if(!agb_track[ch_num]){
                    agb_track[ch_num] = file_memopen(512);
                }


                u8 *mid = tracks[ch_num].mid;
                cur_track = &tracks[ch_num];
                u8 next = 0x80;

                // if(ch_num==0&&row_num==106){
                //     fprintf(stderr,"xx\n");
                // }

                /*
                xm note
                [0] key  键
                [1] insn 乐器
                [2] vol  音量
                [3] 效果
                [4] 效果的参数
                */
                u8 buf[8];
                int off = 0;
                int bit = 0;

                if(tempo!=0){
                    double v = tempo_second(tempo); //1秒多少节拍
                    v = 60/v;        //每个节拍多少帧
                    cur_track->clock += v;
                }

                NOTE_CLOSE:
                while(cur_track->clock>1.0){
                    //执行了多少帧
                    if(cur_track->note_up){
                        //note抬起时
                        cur_track->track_vol *= (cur_track->rel);
                        cur_track->track_vol >>= 8;
                    }
                    else{
                        //note按下时
                        cur_track->track_vol *= (cur_track->dec);
                        cur_track->track_vol >>= 8;
                        if(cur_track->track_vol<cur_track->sus){
                            cur_track->track_vol = cur_track->sus;
                        }
                    }
                    cur_track->clock -= 1.0;
                }

                if(!tracks[ch_num].mid){
                    //全部声道已经关闭时,isend没有改变，则退出处理mid
                    //该声道已经被关闭了,需要跳过当前row
                    file_write(&next,1,1,mid_file);
                    continue;
                }
                isend = 0;
                int num = 0;
                int has_end_note = 0;

                if(tracks[ch_num].note_wait==1){
                    //关闭某个按键
                    //这时候已经为0了
                    tracks[ch_num].note_wait--;
                    cur_track->note_up = 1;

                    if(tracks[ch_num].wait>1){

                        #if !DUMP_TONE_ATT_REL
                        //如果还有等待，则，设置关闭按键
                        buf[off++] = 0x61;  //关闭note
                        bit |= 1;
                        bit |= 0x80;
                        file_write(&bit,1,1,mid_file);
                        file_write(buf,off,1,mid_file);
                        tracks[ch_num].wait--;
                        continue;
                        #else
                        if(cur_track->old_track_vol!=cur_track->track_vol){
                            cur_track->old_track_vol = cur_track->track_vol;
                            double vol = (double)cur_track->vol/(double)2; //xm最大音量为64
                            vol *= cur_track->setvol/127;
                            vol *= (double)(cur_track->track_vol)/255;
                            int v = vol;
                            if(v>64){v=64;};
                            buf[off] = v+0x10;
                            bit |= 0x80|2;
                            file_write(&bit,1,1,mid_file);
                            file_write(buf,off,1,mid_file);
                        }
                        else{
                            bit = 0x80;
                            file_write(&bit,1,1,mid_file);
                        }
                        tracks[ch_num].wait--;
                        continue;
                        #endif
                    }
                    else{
                        //没有关闭note
                        has_end_note = 1;
                    }
                }
                else if(tracks[ch_num].note_wait){
                    tracks[ch_num].note_wait--;
                }


                if(tracks[ch_num].wait){
                    //如果是等待状态，则需要写入一个0x80跳过当前row
                    tracks[ch_num].wait--;
                    #if !DUMP_TONE_ATT_REL
                    file_write(&next,1,1,mid_file);
                    #else
                    if(cur_track->old_track_vol!=cur_track->track_vol){
                        cur_track->old_track_vol = cur_track->track_vol;
                        double vol = (double)cur_track->vol/(double)2; //xm最大音量为64
                        vol *= cur_track->setvol/127;
                        vol *= (double)(cur_track->track_vol)/255;
                        int v = vol;
                        if(v>64){v=64;};
                        buf[off++] = v+0x10;
                        bit |= 0x80|4;
                        file_write(&bit,1,1,mid_file);
                        file_write(buf,off,1,mid_file);
                    }
                    else{
                        file_write(&next,1,1,mid_file);
                    }
                    #endif
                    continue;
                }

                //存在mid指针
                cur_Nxx = 0; //初始化
                while(cur_track->mid&&cur_track->wait==0){
                    //如果等待为0，则更新mid
                    int event = mid[num];
                    if(event==FINE){
                        event = mid[num];
                    }

                    //会记录一个上事件
                    if(event>=0xbd){
                        cur_track->old_event = event;
                    }
                    else if(event<0x80){
                        event = cur_track->old_event;
                    }

                    int pos = file_tell(agb_track[ch_num]);
                    int midlen = mid_event[event](mid+num);

                    if(pos==file_tell(agb_track[ch_num])){
                        //如果没导出数据
                        file_printf(agb_track[ch_num],".byte ");
                        for(pos=0;pos<midlen;pos++){
                            if(pos==0){
                                file_printf(agb_track[ch_num],"0x%X",mid[num+pos]);
                            }
                            else{
                                file_printf(agb_track[ch_num],",0x%X",mid[num+pos]);
                            }
                        }
                        file_printf(agb_track[ch_num],"\n");
                    }

                    num += midlen;
                }
                if(tracks[ch_num].mid){
                    //mid还存在
                    tracks[ch_num].mid += num;
                }

                cur_track->wait -= 1;

                int note_down = 0;
                int note_up = 0;

                //等待帧数
                if(cur_track->note){
                    //按下按键
                    //buf[off++] = cur_track->note - 36 + 1;
                    buf[off++] = toxm_note(cur_track->note);
                    cur_track->last_note = cur_track->note;
                    cur_track->note = 0;
                    cur_track->note_wait = cur_track->cur_Nxx;
                    note_down = 1;
                    bit |= 1;
                }
                else if(cur_Nxx){
                    //如果又Nxx时，就表示，继续上一个按键
                    buf[off++] = toxm_note(cur_track->note);
                    note_down = 1;
                    bit |= 1;
                }
                else if(has_end_note){
                    //关闭
                    note_up = 1;
                    #if !DUMP_TONE_ATT_REL
                    buf[off++] = 0x61;  //关闭note
                    bit |= 1;
                    #else
                    #endif
                }

                if(note_down){
                    /*
                    处理当前音量
                    */
                    if(cur_track->tone){
                        if(cur_track->tone->t.typ==0||cur_track->tone->t.typ==1||cur_track->tone->t.typ==8){
                            cur_track->att = cur_track->tone->t.att;
                            cur_track->dec = cur_track->tone->t.dec;
                            cur_track->sus = cur_track->tone->t.sus;
                            cur_track->rel = cur_track->tone->t.rel;
                            cur_track->track_vol = cur_track->vol;
                        }
                    }
                    cur_track->note_up = 0;
                }
                else if(note_up){
                    cur_track->note_up = 1;
                    if(has_end_note){
                        //设置关闭时音量
                        cur_track->track_vol = cur_track->sus;
                        cur_track->note_wait = 1;
                        cur_track->wait ++;     //跳转处理需要加1
                        goto NOTE_CLOSE;
                    }
                }

                #if DUMP_TONE_RHYTHM_SET
                if((bit&1)&&cur_track->tone->t.typ==0x80){
                    //为split音色
                    int note = cur_track->last_note;
                    note += cur_track->tone->ref.base_note;
                    _0x80* tone = (_0x80*)cur_track->tone->ref.data;
                    tone = fix_rom(tone);
                    tone += note;
                    ToneData*t = (ToneData*)tone;    //一般这个tone需要是采样文件
                    if(t->typ==0||t->typ==8){
                        WaveData*wav = fix_rom(t->wav);
                        cur_track->insn = _add_insn((ToneDataArr*)tone,wav);
                        // if(t->typ==8){
                        buf[0] = xm_Cn5;    //直接等于Cn5

                        cur_track->att = t->att;
                        cur_track->dec = t->dec;
                        cur_track->sus = t->sus;
                        cur_track->rel = t->rel;
                        cur_track->track_vol = cur_track->vol;

                        // }
                    }
                }
                #endif
                #if DUMP_TONE_SPLIT
                else if((bit&1)&&cur_track->tone->t.typ==0x40){
                    int note = cur_track->last_note;
                    u8 *splitbl = fix_rom(cur_track->tone->s.st);
                    int index = splitbl[note];
                    ToneDataArr *arr = fix_rom(cur_track->tone->s.ta);
                    arr += index;
                    if(arr->t.typ==0||arr->t.typ==8){
                        //声音样本
                        WaveData*wd = fix_rom(arr->t.wav);

                        cur_track->att = arr->t.att;
                        cur_track->dec = arr->t.dec;
                        cur_track->sus = arr->t.sus;
                        cur_track->rel = arr->t.rel;
                        cur_track->track_vol = cur_track->vol;

                        if(0){
                            //写入wav文件
                            if((wd->stat&0xff)==0){
                                char name[BUFSIZ];
                                name[sprintf(name,"%08X.wav",arr->t.wav)] = 0;
                                int i;
                                for(i=0;i<wd->size;i++){
                                    wd->data[i] += 0x80;
                                }
                                write_wav(name,agb2freq(wd->freq,arr->t.key),wd->data,wd->size);
                                for(i=0;i<wd->size;i++){
                                    wd->data[i] -= 0x80;
                                }
                            }
                        }

                        cur_track->insn = _add_insn(arr,wd);
                        buf[0] = _toxm_note(note,arr->t.key);
                        int freq = MidiKey2Freq(wd,note,0);
                        int wavfreq = agb2freq(wd->freq,arr->t.key);
                        int midfreq = c5_note_freq(wavfreq,buf[0]);
                        fprintf(stderr,"xfreq:%d,%d,%d\n",freq,wavfreq,midfreq);
                    }
                }
                #endif
                if(cur_track->insn!=cur_track->oldinsn){
                    //需要判断乐器是否是曾经的乐器
                    //更换乐器
                    buf[off++] = cur_track->insn;
                    cur_track->oldinsn = cur_track->insn;
                    bit |= 2;
                }

                /*
                处理轨道的音量
                */
                if(!cur_track->note_up){
                    //非按下
                    if(cur_track->track_vol<cur_track->sus){
                        //维持在这个区域
                        cur_track->track_vol = cur_track->sus;
                    }
                }

                if(bit&1){
                    //按下note键
                    int vol = cur_track->vol>>1;    //主音量最大为127
                    if(cur_track->setvol){
                        double v = (double)127 / (double)cur_track->setvol;
                        v *= (double)vol;
                        v *= (double)cur_track->track_vol / 255.0;
                        cur_track->old_track_vol = cur_track->track_vol;
                        vol = v;
                    }
                    if(vol>64){
                        vol = 64;
                    }

                    buf[off++] = vol + 0x10;
                    bit |= 4;
                }

                if(tempo!=oldtempo&&0){
                    //更改速度
                    //xm的tempo跟agb的不同
                    //0xfxx
                    buf[off++] = 0xf;
                    buf[off++] = tempo;
                    oldtempo = tempo;
                    bit |= 8;
                }

                if(bit==(1|2|4)){
                    //最后一个特效，可以设置为0000
                    buf[off++] = 0;
                    buf[off++] = 0;
                    bit |= 8;
                }

                if(
                    bit==(1|2|4|8) ||
                    bit==(1|2|4|8|16)
                ){
                    //写入5个字节
                    file_write(buf,5,1,mid_file);
                }
                else if(!bit){
                    //写入空值
                    buf[off++] = 0x80;
                    file_write(buf,1,1,mid_file);
                }
                else{
                    //按条件写入值
                    bit |= 0x80;
                    file_write(&bit,1,1,mid_file);
                    file_write(buf,off,1,mid_file);
                }
            }
        }



        #endif
        #if 0
        //导出音色表
        int i=0;
        for(i=0;i<120&&arr_base;i++){
            ToneDataArr*arr = arr_base + i;
            if(arr->t.typ==0||arr->t.typ==1){
                WaveData *wd =  fix_rom(arr->t.wav);
                if(wd&&wd->freq>128){
                    //把wav数据导出
                    fprintf(stderr,"ToneData:%X\n",arr->t.wav);
                    char buf[BUFSIZ];
                    int len = sprintf(buf,"%X.wav",arr->t.wav);
                    buf[len] = 0;
                    /*
                    需要计算频率,先获得key键
                    频率 = 原始频率 * 2^((180-中心key键)/12)
                    */
                    double m = (double)(180 - arr->t.key) / (double)12;
                    m = pow(2,m);
                    int freq = ((double)wd->freq / m) + 0.5;
                    int num;
                    for(num=0;num<wd->size;num++){
                        wd->data[num] += 0x80;
                    }

                    #if 1
                    //write_wav(buf,freq,wd->data,wd->size);
                    #endif
                    #if 1
                    //生成乐器文件
                    if(insn_num<90){
                        for(num=0;num<wd->size;num++){
                            wd->data[num] += 0x80;
                        }
                        xm_gen_insn(insn_file,(u32)arr->t.wav,wd->data,wd->size,freq);
                        insn_num++;
                    }
                    #endif
                    wd->freq = 0;
                    //exit(0);
                }
            }
        }
        #endif

        //把当前记录的mid写入到pat文件中
        xm_gen_pat(pat_file,mid_file);
        pat_num++;

        //file_seek(pat_file,0,SEEK_SET);
        FILE*xm_file = fopen("test.xm","wb+");
        double v = tempo_second(tempo); //每秒多少个row,如每秒25个row
        v = 1 / v;     //每个row需要多少秒
        xm_gen(xm_file,insn_file,insn_num,so->trks,pat_file,pat_num,2.5/v);
        fclose(xm_file);
    }
}

static void* proc_music(){
    SongTable *song_table = 0;
    u32 addr = find_rom();
    insn_file = file_memopen(512);
    mid_file = file_memopen(512);
    pat_file = file_memopen(512);
    if(!addr){
        fprintf(stderr,"no find m4aSongNumStart:\n");
    }
    else{
        fprintf(stderr,"find m4aSongNumStart:%x\n",addr);
    }
    addr += 0x28;
    if(fix_rom(addr)){
        song_table = fix_rom(*(u32*)fix_rom(addr));
    }
    int idx = 0;
    if(dump_index){
        song_table += (dump_index - 1);
    }
    /*遇到0则结束*/
    while(song_table->so){
        //fprintf(stderr,"[%d]=%08X,%d,%d\n",idx,song_table->so,song_table->ms,song_table->me);

        song_proc(song_table);
        if(dump_index){
            break;
        }
        idx++;
        song_table++;
    }
}

int main(int argc,char**argv){
    rom = rom_data(argv[1]);
    if(!rom){
        fprintf(stderr,"error rom:%s\n",argv[1]);
        return -1;
    }
    dump_index = 14+1;  //wario
    //dump_index = 13+1;  //agb2000
    proc_music();
    fprintf(stderr,"agb out\n");
    _xm_write_d(stderr,agb_track[0]);
    return 0;
}
